#Required libraries

# Tidyverse for data science and exploration
require(dplyr)
Loading required package: dplyr

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
require(tidyr)
Loading required package: tidyr
require(readr)
Loading required package: readr
require(tibble)
Loading required package: tibble
require(stringr)
Loading required package: stringr
require(purrr)
Loading required package: purrr
require(forcats)
Loading required package: forcats
require(rlang)
Loading required package: rlang

Attaching package: ‘rlang’

The following objects are masked from ‘package:purrr’:

    %@%, as_function, flatten, flatten_chr, flatten_dbl, flatten_int, flatten_lgl, flatten_raw, invoke, list_along,
    modify, prepend, splice
# enhances tidyverse
require(tidylog) # additional logging
Loading required package: tidylog

Attaching package: ‘tidylog’

The following objects are masked from ‘package:tidyr’:

    drop_na, fill, gather, pivot_longer, pivot_wider, replace_na, spread, uncount

The following objects are masked from ‘package:dplyr’:

    add_count, add_tally, anti_join, count, distinct, distinct_all, distinct_at, distinct_if, filter, filter_all,
    filter_at, filter_if, full_join, group_by, group_by_all, group_by_at, group_by_if, inner_join, left_join, mutate,
    mutate_all, mutate_at, mutate_if, relocate, rename, rename_all, rename_at, rename_if, rename_with, right_join,
    sample_frac, sample_n, select, select_all, select_at, select_if, semi_join, slice, slice_head, slice_max,
    slice_min, slice_sample, slice_tail, summarise, summarise_all, summarise_at, summarise_if, summarize,
    summarize_all, summarize_at, summarize_if, tally, top_frac, top_n, transmute, transmute_all, transmute_at,
    transmute_if, ungroup

The following object is masked from ‘package:stats’:

    filter
require(magrittr) # additional data pipe syntax
Loading required package: magrittr

Attaching package: ‘magrittr’

The following object is masked from ‘package:rlang’:

    set_names

The following object is masked from ‘package:purrr’:

    set_names

The following object is masked from ‘package:tidyr’:

    extract
# for reading data in multiple formats
require(readxl)
Loading required package: readxl
require(haven)
Loading required package: haven
# visual analysis
require(ggplot2)
Loading required package: ggplot2
Want to understand how all the pieces fit together? Read R for Data Science: https://r4ds.had.co.nz/
require(GGally) # extensions to ggplot
Loading required package: GGally
Registered S3 method overwritten by 'GGally':
  method from   
  +.gg   ggplot2
require(gt) # well formatted tables
Loading required package: gt
# client-side interactive publishable graphics
require(plotly)
Loading required package: plotly
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following objects are masked from ‘package:tidylog’:

    distinct, filter, group_by, mutate, rename, select, slice, summarise, transmute, ungroup

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
require(leaflet)
Loading required package: leaflet
require(crosstalk)
Loading required package: crosstalk
require(htmlwidgets)
Loading required package: htmlwidgets
# server-side interactive graphics
require(shiny)
Loading required package: shiny

Attaching package: ‘shiny’

The following object is masked from ‘package:crosstalk’:

    getDefaultReactiveDomain
require(shinyjs)
Loading required package: shinyjs
Find out advanced usage of shinyjs:
    https://deanattali.com/shinyjs/advanced

Attaching package: ‘shinyjs’

The following object is masked from ‘package:shiny’:

    runExample

The following object is masked from ‘package:gt’:

    html

The following objects are masked from ‘package:methods’:

    removeClass, show
# Canned Interactive EDA 
require(ExPanDaR)
Loading required package: ExPanDaR

Exploring KU Book Processing Charges

# read KU data frame
KUbpc.df <- read_csv("Public Data/openapc-de/data/bpc.csv")
Parsed with column specification:
cols(
  institution = col_character(),
  period = col_double(),
  euro = col_double(),
  doi = col_character(),
  backlist_oa = col_logical(),
  publisher = col_character(),
  book_title = col_character(),
  isbn = col_character(),
  isbn_print = col_character(),
  isbn_electronic = col_character(),
  license_ref = col_character(),
  indexed_in_crossref = col_logical(),
  doab = col_logical()
)
# read DOAB metadata

source('Public Data/DOAB/doabingest.R')
DOABmeta.df <- doabFetch()
embedded nul(s) found in input


head(KUbpc.df)
head(summary(KUbpc.df))
 institution            period          euro          doi            backlist_oa      publisher          book_title       
 Length:938         Min.   :2017   Min.   :1075   Length:938         Mode :logical   Length:938         Length:938        
 Class :character   1st Qu.:2017   1st Qu.:1875   Class :character   FALSE:357       Class :character   Class :character  
 Mode  :character   Median :2018   Median :1981   Mode  :character   TRUE :581       Mode  :character   Mode  :character  
                    Mean   :2018   Mean   :4368                                                                           
                    3rd Qu.:2019   3rd Qu.:8250                                                                           
                    Max.   :2020   Max.   :8978                                                                           
     isbn            isbn_print        isbn_electronic    license_ref        indexed_in_crossref    doab        
 Length:938         Length:938         Length:938         Length:938         Mode :logical       Mode :logical  
 Class :character   Class :character   Class :character   Class :character   FALSE:127           FALSE:44       
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   TRUE :811           TRUE :894      
                                                                                                                
                                                                                                                
                                                                                                                
ggplot(data = KUbpc.df, aes(KUbpc.df$institution)) + geom_bar() 


ggplot(data = KUbpc.df, aes(KUbpc.df$euro)) + geom_histogram()

General Exploratory Data Analysis


ggplot(data = KUbpc.df) + geom_bar(mapping = aes(x = KUbpc.df$doab))


# Date to Doab
date_doab <- KUbpc.df %>% ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$period, colour = KUbpc.df$doab)) + geom_freqpoly(binwidth = 0.1)
ggplotly(date_doab)


# publisher_euro <- KUbpc.df %>% 
# ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$publisher, colour = KUbpc.df$euro)) + geom_freqpoly(binwidth = 0.1)

# Institution to Euro
institution_euro <- KUbpc.df %>% ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$euro)) + geom_freqpoly(mapping = aes(colour = KUbpc.df$institution), binwidth = 500)

ggplotly(institution_euro)

NA

Idea: Publishers vs. Charges

Question: How do the top 25% of publishers divide up charges (in Euro)?

Observation: Charges are grouped around ~2000 Euros and ~8000 Euros.


publisher_counts <- KUbpc.df %>%
    group_by(publisher) %>%
    tally
tally: now 110 rows and 2 columns, ungrouped
sorted_counts = arrange(publisher_counts, desc(n))

total_n = sum(sorted_counts$n)
quarter_n = 0.25 * total_n
new_n = sum(sorted_counts$n[0:6])

sorted_counts %>% filter(n > 24)

# filtered <- filter(KUbpc.df$publisher %in% sorted_counts$publisher)

filtered <- filter(KUbpc.df, KUbpc.df$publisher == 'transcript Verlag' |
                     KUbpc.df$publisher == 'Duke University Press' |
                     KUbpc.df$publisher == 'University of Michigan Press' |
                     KUbpc.df$publisher == 'Manchester University Press' |
                     KUbpc.df$publisher == 'Pluto Press' |
                     KUbpc.df$publisher == 'Liverpool University Press')

head(filtered)

euro_publisher <- filtered %>% 
  ggplot(data = filtered, mapping = aes(x = filtered$publisher, y = filtered$euro), 
         aes(x = filtered$publisher, y = filtered$euro)) + 
  # geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) + 
  geom_count(aes(color = ..n.., group = euro)) + 
  scale_size_area(max_size = 10) + 
  theme(axis.text = element_text(size = rel(0.75))) +
  labs(title = "How Publishers Divide Charges", x = "Top 25% of Publishers", y = "Price (Euro)", color = 'Number of Copies') +
  scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))

# ggplot:
ggplotly(euro_publisher)
`group_by_()` is deprecated as of dplyr 0.7.0.
Please use `group_by()` instead.
See vignette('programming') for more help
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.

# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$publisher, y = filtered$euro)) + 
  geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) + 
  labs(title = "How Publishers Divide Charges", x = "Top 25% of Publishers", y = "Price (Euro)", color = 'Number of Copies') +
  scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))
cross_ft <- bscols(
  filter_select("publisher", "Select a publisher", ft, ~publisher),
  ggplotly(gg_ft, dynamicTicks = TRUE),
  widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)


# shared_euro_publisher <- SharedData$new(filtered)
# leaflet(shared_euro_publisher) %>% addMarkers()
# data.table::data.table(shared_euro_publisher)

Idea: Publishers’ Charges vs. Year/OA Type

Sub-Question: What best explains the particular division of charges? (Year, OA Type)

Observation: The low and high charge groups seem to be defined by the type of OA business model, whereas the slight differences within each group seem to be defined by the year.


head(filtered)

# Does Type of OA impact the particular division of charges?

euro_oa_publisher <- filtered %>% 
  ggplot(data = filtered, mapping = aes(x = filtered$backlist_oa, y = filtered$euro), 
         aes(x = filtered$backlist_oa, y = filtered$euro)) + 
  geom_count(aes(color = ..n.., group = euro)) + 
  scale_size_area(max_size = 10) + 
  theme(axis.text = element_text(size = rel(0.75))) +
  labs(title = "How OA Impacts Price Division of Charges", x = "Type of OA", y = "Price (Euro)", color = 'Number of Copies')

# ggplot:
ggplotly(euro_oa_publisher)


# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$backlist_oa, y = filtered$euro)) + 
  geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) + 
  labs(title = "How OA Impacts Division of Charges", x = "Type of OA", y = "Price (Euro)", color = 'Number of Copies')
cross_ft <- bscols(
  filter_select("publisher", "Select a publisher", ft, ~publisher),
  ggplotly(gg_ft, dynamicTicks = TRUE),
  widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)



# Does Year impact the particular division of charges?

euro_year_publisher <- filtered %>% 
  ggplot(data = filtered, mapping = aes(x = filtered$period, y = filtered$euro), 
         aes(x = filtered$period, y = filtered$euro)) + 
  geom_count(aes(color = ..n.., group = euro)) + 
  scale_size_area(max_size = 10) + 
  theme(axis.text = element_text(size = rel(0.75))) +
  labs(title = "How Year Impacts Price Division of Charges", x = "Year", y = "Price (Euro)", color = 'Number of Copies')

# ggplot:
ggplotly(euro_year_publisher)


# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$period, y = filtered$euro)) + 
  geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) + 
  labs(title = "How Year Impacts Division of Charges", x = "Year", y = "Price (Euro)", color = 'Number of Copies')
cross_ft <- bscols(
  filter_select("publisher", "Select a publisher", ft, ~publisher),
  ggplotly(gg_ft, dynamicTicks = TRUE),
  widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)

NA
NA

Idea: Publishers vs. OA

Question: What type of business model do the top 25% publishers use?

Observation: Most have a higher proportion of True (moved to OA from traditional publishing) than False (already published OA).


oa_type <- filtered %>% 
  ggplot(data = filtered, mapping = aes(x = filtered$publisher, colour = filtered$backlist_oa), fill = filtered$backlist_oa) +
  geom_bar(position = "fill", width = 0.7, fill="#EAEAEA") +
  labs(title = "Business Model OA for Publishers", x = "Top 25% of Publishers", y = "Proportion", color = 'Types of OA') +
  theme(axis.text = element_text(size = rel(0.75))) +
  scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17)) +
  scale_color_brewer(palette = "Set1")

ggplotly(oa_type)


# crosstalk:
ft <- highlight_key(filtered)
oa_ft <- ggplot(data = ft, mapping = aes(x = ft$publisher, colour = ft$backlist_oa), fill = ft$backlist_oa) +
  geom_bar(position = "fill", width = 0.7) +
  labs(title = "Business Model OA for Publishers", x = "Top 25% of Publishers", y = "Proportion of Backlist OA", color = 'Types of OA') +
  theme(axis.text = element_text(size = rel(0.75))) +
  scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))
# cross_oa_ft <- bscols(
#   filter_select("publisher", "Select a publisher", ft, ~publisher),
#   ggplotly(oa_ft, dynamicTicks = TRUE),
#   # widths = c(12, 12)
# )

# bscols(cross_oa_ft)

Idea: Publishers’ OA vs. Year

Question: Did OA business models of the top 25% publishers change per year?

Observation:


oa_time <- function(pub_name) {
  pub_ft <- filter(filtered, filtered$publisher == pub_name)
  
  pub_oa <- pub_ft %>% 
    ggplot(data = pub_ft, mapping = aes(x = pub_ft$period, colour = pub_ft$backlist_oa), fill = pub_ft$backlist_oa) +
    geom_bar(position = "fill", width = 0.7, fill="#EAEAEA") +
    labs(title = paste(pub_name, "'s OA Through the Years", sep = ""), 
         x = "Years", y = "Proportion of Backlist OA", color = 'Types of OA') +
    theme(axis.text = element_text(size = rel(0.75))) +
    scale_x_discrete(limits=c(2017, 2018, 2019)) +
    scale_color_brewer(palette = "Set1")

  ggplotly(pub_oa)
  
}

top25_list = c("transcript Verlag", "Duke University Press", "University of Michigan Press", "Manchester University Press", "Pluto Press", "Liverpool University Press")

oa_time("transcript Verlag")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

oa_time("Duke University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

oa_time("University of Michigan Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

oa_time("Manchester University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

oa_time("Pluto Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

oa_time("Liverpool University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

Idea: Revenue vs. OA

Question: What total revenue are publishers receiving each year?

Observation:


# Finding total revenue for each publisher

revenue_finder <- function(pub_name) {
  pub_filtered <- filter(filtered, filtered$publisher == pub_name)
  rev = sum(pub_filtered$euro)
}

revenue_df <- data.frame("publisher" = top25_list)
revenue_list <- c()

for (i in top25_list) {
  revenue_list<-c(revenue_list,revenue_finder(i))
}

revenue_df$revenue <- c(revenue_list)
print(revenue_df)

# ggplot:
publisher_revenue <- revenue_df %>%
  ggplot(data = revenue_df, mapping = aes(x = revenue_df$publisher, y = revenue_df$revenue), fill = revenue_df$revenue) +
  geom_col() +
  labs(title = "Total Revenue for Publishers", x = "Top 25% of Publishers", y = "Revenue (Euro)", color = 'Types of OA') +
  theme(axis.text = element_text(size = rel(0.75))) +
  scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17)) +
  scale_fill_brewer(palette = "Set1")

ggplotly(publisher_revenue)
Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.Use of `revenue_df$revenue` is discouraged. Use `revenue` instead.

Idea: Revenue vs. OA

Question: What revenue are publishers receiving per year?

Observation:


# Finding total revenue for each publisher

revlist_2017 <- c()
revlist_2018 <- c()
revlist_2019 <- c()

revlist <- c()

for (name in top25_list) {
  pub_name <- filter(filtered, filtered$publisher == name)
  rev_2017 = sum(pub_name[pub_name$period == 2017,]$euro)
  revlist_2017 <- c(revlist_2017, rev_2017)
  rev_2018 = sum(pub_name[pub_name$period == 2018,]$euro)
  revlist_2018 <- c(revlist_2018, rev_2018)
  rev_2019 = sum(pub_name[pub_name$period == 2019,]$euro)
  revlist_2019 <- c(revlist_2019, rev_2019)
}

revenue_df <- data.frame("publisher" = top25_list)
revenue_df$'2017' <- c(revlist_2017)
revenue_df$'2018' <- c(revlist_2018)
revenue_df$'2019' <- c(revlist_2019)

print(revenue_df)

revenue_year <- c(revenue_df$'2017', revenue_df$'2018', revenue_df$'2019')
year <- c('2017', '2018', '2019')

# ggplot:
pub_year_revenue1 <- revenue_df %>%
  
  ggplot(data = revenue_df, mapping = aes(x = '2017', y = revenue_df$'2017', fill = revenue_df$publisher)) +
  geom_bar(position="dodge", stat="identity") +
  labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
  theme(axis.text = element_text(size = rel(0.75)))

ggplotly(pub_year_revenue1)
Use of `revenue_df$"2017"` is discouraged. Use `2017` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.

pub_year_revenue2 <- revenue_df %>%
  
  ggplot(data = revenue_df, mapping = aes(x = '2018', y = revenue_df$'2018', fill = revenue_df$publisher)) +
  geom_bar(position="dodge", stat="identity") +
  labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
  theme(axis.text = element_text(size = rel(0.75)))

ggplotly(pub_year_revenue2)
Use of `revenue_df$"2018"` is discouraged. Use `2018` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.

pub_year_revenue3 <- revenue_df %>%
  
  ggplot(data = revenue_df, mapping = aes(x = '2019', y = revenue_df$'2019', fill = revenue_df$publisher)) +
  geom_bar(position="dodge", stat="identity") +
  labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
  theme(axis.text = element_text(size = rel(0.75)))

ggplotly(pub_year_revenue3)
Use of `revenue_df$"2019"` is discouraged. Use `2019` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.

Continued, tried putting it into one graph.


revlist <- c()
revlist_2017 <- c()
revlist_2018 <- c()
revlist_2019 <- c()

for (name in top25_list) {
  pub_name <- filter(filtered, filtered$publisher == name)
  rev_2017 = sum(pub_name[pub_name$period == 2017,]$euro)
  revlist_2017 <- c(revlist_2017, rev_2017)
  rev_2018 = sum(pub_name[pub_name$period == 2018,]$euro)
  revlist_2018 <- c(revlist_2018, rev_2018)
  rev_2019 = sum(pub_name[pub_name$period == 2019,]$euro)
  revlist_2019 <- c(revlist_2019, rev_2019)
}

revlist <- c(revlist_2017, revlist_2018, revlist_2019)

print(revlist)
 [1] 127420 153491  43044  96849  51824  48131  58125  61875  54750  36000  53625  21375  51750  58125  36000  17625  40500  42375
nrev <- matrix(revlist, ncol=6, byrow=TRUE)
colnames(nrev) <- top25_list
rownames(nrev) <- c("2017", "2018", "2019")
nrev <- as.table(nrev)
nrev <- as.data.frame.matrix(nrev)

print(nrev)

#, nrev$`Duke University Press`, nrev$`University of Michigan Press`, nrev$`Pluto Press`, nrev$`Manchester University Press`, nrev$`Liverpool University Press`

pub_year_rev <- nrev %>%
  
  ggplot(data = nrev, mapping = aes(x = c("2017", "2018", "2019"), y = c(nrev$"transcript Verlag"), fill = nrev$publisher)) +
  geom_bar(position="dodge", stat="identity") +
  labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
  theme(axis.text = element_text(size = rel(0.75)))

ggplotly(pub_year_rev)
Use of `nrev$"transcript Verlag"` is discouraged. Use `transcript Verlag` instead.Use of `nrev$publisher` is discouraged. Use `publisher` instead.

Idea: DOAB analysis

Question: What is the average time gap between year of publication and added on date?

Observation:


DOABmeta.df <- filter(DOABmeta.df, is.na(DOABmeta.df$Year.of.publication))
print(DOABmeta.df$Year.of.publication[1:4])
[1] <NA> <NA> <NA> <NA>
694 Levels:  ...
gap = mean(DOABmeta.df$Added.on.date - DOABmeta.df$Year.of.publication[1:3])
‘-’ not meaningful for factors
print(gap)
[1] NA

Comparison of charges by year and backlist

Interactive charges exploration

### Interactive Dataset Exploration 
LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgQW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQojUmVxdWlyZWQgbGlicmFyaWVzCgojIFRpZHl2ZXJzZSBmb3IgZGF0YSBzY2llbmNlIGFuZCBleHBsb3JhdGlvbgpyZXF1aXJlKGRwbHlyKQpyZXF1aXJlKHRpZHlyKQpyZXF1aXJlKHJlYWRyKQpyZXF1aXJlKHRpYmJsZSkKcmVxdWlyZShzdHJpbmdyKQpyZXF1aXJlKHB1cnJyKQpyZXF1aXJlKGZvcmNhdHMpCnJlcXVpcmUocmxhbmcpCgojIGVuaGFuY2VzIHRpZHl2ZXJzZQpyZXF1aXJlKHRpZHlsb2cpICMgYWRkaXRpb25hbCBsb2dnaW5nCnJlcXVpcmUobWFncml0dHIpICMgYWRkaXRpb25hbCBkYXRhIHBpcGUgc3ludGF4CgoKIyBmb3IgcmVhZGluZyBkYXRhIGluIG11bHRpcGxlIGZvcm1hdHMKcmVxdWlyZShyZWFkeGwpCnJlcXVpcmUoaGF2ZW4pCgojIHZpc3VhbCBhbmFseXNpcwpyZXF1aXJlKGdncGxvdDIpCnJlcXVpcmUoR0dhbGx5KSAjIGV4dGVuc2lvbnMgdG8gZ2dwbG90CnJlcXVpcmUoZ3QpICMgd2VsbCBmb3JtYXR0ZWQgdGFibGVzCiMgY2xpZW50LXNpZGUgaW50ZXJhY3RpdmUgcHVibGlzaGFibGUgZ3JhcGhpY3MKcmVxdWlyZShwbG90bHkpCnJlcXVpcmUobGVhZmxldCkKcmVxdWlyZShjcm9zc3RhbGspCnJlcXVpcmUoaHRtbHdpZGdldHMpCiMgc2VydmVyLXNpZGUgaW50ZXJhY3RpdmUgZ3JhcGhpY3MKcmVxdWlyZShzaGlueSkKcmVxdWlyZShzaGlueWpzKQojIENhbm5lZCBJbnRlcmFjdGl2ZSBFREEgCnJlcXVpcmUoRXhQYW5EYVIpCgoKYGBgCiMjIEV4cGxvcmluZyBLVSBCb29rIFByb2Nlc3NpbmcgQ2hhcmdlcwpgYGB7ciAgfQojIHJlYWQgS1UgZGF0YSBmcmFtZQpLVWJwYy5kZiA8LSByZWFkX2NzdigiUHVibGljIERhdGEvb3BlbmFwYy1kZS9kYXRhL2JwYy5jc3YiKQojIHJlYWQgRE9BQiBtZXRhZGF0YQoKc291cmNlKCdQdWJsaWMgRGF0YS9ET0FCL2RvYWJpbmdlc3QuUicpCkRPQUJtZXRhLmRmIDwtIGRvYWJGZXRjaCgpCmBgYApgYGB7ciAgfQoKCmhlYWQoS1VicGMuZGYpCmhlYWQoc3VtbWFyeShLVWJwYy5kZikpCgpnZ3Bsb3QoZGF0YSA9IEtVYnBjLmRmLCBhZXMoS1VicGMuZGYkaW5zdGl0dXRpb24pKSArIGdlb21fYmFyKCkgCgpnZ3Bsb3QoZGF0YSA9IEtVYnBjLmRmLCBhZXMoS1VicGMuZGYkZXVybykpICsgZ2VvbV9oaXN0b2dyYW0oKQoKYGBgCiMjIEdlbmVyYWwgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcwpgYGB7ciAgfQoKZ2dwbG90KGRhdGEgPSBLVWJwYy5kZikgKyBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBLVWJwYy5kZiRkb2FiKSkKCiMgRGF0ZSB0byBEb2FiCmRhdGVfZG9hYiA8LSBLVWJwYy5kZiAlPiUgZ2dwbG90KGRhdGEgPSBLVWJwYy5kZiwgbWFwcGluZyA9IGFlcyh4ID0gS1VicGMuZGYkcGVyaW9kLCBjb2xvdXIgPSBLVWJwYy5kZiRkb2FiKSkgKyBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMC4xKQpnZ3Bsb3RseShkYXRlX2RvYWIpCgojIHB1Ymxpc2hlcl9ldXJvIDwtIEtVYnBjLmRmICU+JSAKIyBnZ3Bsb3QoZGF0YSA9IEtVYnBjLmRmLCBtYXBwaW5nID0gYWVzKHggPSBLVWJwYy5kZiRwdWJsaXNoZXIsIGNvbG91ciA9IEtVYnBjLmRmJGV1cm8pKSArIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAwLjEpCgojIEluc3RpdHV0aW9uIHRvIEV1cm8KaW5zdGl0dXRpb25fZXVybyA8LSBLVWJwYy5kZiAlPiUgZ2dwbG90KGRhdGEgPSBLVWJwYy5kZiwgbWFwcGluZyA9IGFlcyh4ID0gS1VicGMuZGYkZXVybykpICsgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG91ciA9IEtVYnBjLmRmJGluc3RpdHV0aW9uKSwgYmlud2lkdGggPSA1MDApCgpnZ3Bsb3RseShpbnN0aXR1dGlvbl9ldXJvKQoKYGBgCiMjIElkZWE6IFB1Ymxpc2hlcnMgdnMuIENoYXJnZXMKIyMgUXVlc3Rpb246IEhvdyBkbyB0aGUgdG9wIDI1JSBvZiBwdWJsaXNoZXJzIGRpdmlkZSB1cCBjaGFyZ2VzIChpbiBFdXJvKT8KIyMgT2JzZXJ2YXRpb246IENoYXJnZXMgYXJlIGdyb3VwZWQgYXJvdW5kIH4yMDAwIEV1cm9zIGFuZCB+ODAwMCBFdXJvcy4gCmBgYHtyICB9CgpwdWJsaXNoZXJfY291bnRzIDwtIEtVYnBjLmRmICU+JQogICAgZ3JvdXBfYnkocHVibGlzaGVyKSAlPiUKICAgIHRhbGx5Cgpzb3J0ZWRfY291bnRzID0gYXJyYW5nZShwdWJsaXNoZXJfY291bnRzLCBkZXNjKG4pKQoKdG90YWxfbiA9IHN1bShzb3J0ZWRfY291bnRzJG4pCnF1YXJ0ZXJfbiA9IDAuMjUgKiB0b3RhbF9uCm5ld19uID0gc3VtKHNvcnRlZF9jb3VudHMkblswOjZdKQoKc29ydGVkX2NvdW50cyAlPiUgZmlsdGVyKG4gPiAyNCkKCiMgZmlsdGVyZWQgPC0gZmlsdGVyKEtVYnBjLmRmJHB1Ymxpc2hlciAlaW4lIHNvcnRlZF9jb3VudHMkcHVibGlzaGVyKQoKZmlsdGVyZWQgPC0gZmlsdGVyKEtVYnBjLmRmLCBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ3RyYW5zY3JpcHQgVmVybGFnJyB8CiAgICAgICAgICAgICAgICAgICAgIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAnRHVrZSBVbml2ZXJzaXR5IFByZXNzJyB8CiAgICAgICAgICAgICAgICAgICAgIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAnVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBQcmVzcycgfAogICAgICAgICAgICAgICAgICAgICBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ01hbmNoZXN0ZXIgVW5pdmVyc2l0eSBQcmVzcycgfAogICAgICAgICAgICAgICAgICAgICBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ1BsdXRvIFByZXNzJyB8CiAgICAgICAgICAgICAgICAgICAgIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAnTGl2ZXJwb29sIFVuaXZlcnNpdHkgUHJlc3MnKQoKaGVhZChmaWx0ZXJlZCkKCmV1cm9fcHVibGlzaGVyIDwtIGZpbHRlcmVkICU+JSAKICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkLCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRwdWJsaXNoZXIsIHkgPSBmaWx0ZXJlZCRldXJvKSwgCiAgICAgICAgIGFlcyh4ID0gZmlsdGVyZWQkcHVibGlzaGVyLCB5ID0gZmlsdGVyZWQkZXVybykpICsgCiAgIyBnZW9tX2NvdW50KGFlcyhjb2xvciA9IC4ubi4uLCBzaXplID0gYWZ0ZXJfc3RhdChwcm9wKSwgZ3JvdXAgPSBldXJvKSkgKyAKICBnZW9tX2NvdW50KGFlcyhjb2xvciA9IC4ubi4uLCBncm91cCA9IGV1cm8pKSArIAogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDEwKSArIAogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKwogIGxhYnModGl0bGUgPSAiSG93IFB1Ymxpc2hlcnMgRGl2aWRlIENoYXJnZXMiLCB4ID0gIlRvcCAyNSUgb2YgUHVibGlzaGVycyIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHN0cl9yZXBsYWNlX2FsbCh4LCAiZm9vIiwgIiAiKSwgd2lkdGggPSAxNykpCgojIGdncGxvdDoKZ2dwbG90bHkoZXVyb19wdWJsaXNoZXIpCgojIGNyb3NzdGFsazoKZnQgPC0gaGlnaGxpZ2h0X2tleShmaWx0ZXJlZCkKZ2dfZnQgPC0gZ2dwbG90KGRhdGEgPSBmdCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcHVibGlzaGVyLCB5ID0gZmlsdGVyZWQkZXVybykpICsgCiAgZ2VvbV9jb3VudChhZXMoY29sb3IgPSAuLm4uLiwgc2l6ZSA9IGFmdGVyX3N0YXQocHJvcCksIGdyb3VwID0gZXVybykpICsgCiAgbGFicyh0aXRsZSA9ICJIb3cgUHVibGlzaGVycyBEaXZpZGUgQ2hhcmdlcyIsIHggPSAiVG9wIDI1JSBvZiBQdWJsaXNoZXJzIiwgeSA9ICJQcmljZSAoRXVybykiLCBjb2xvciA9ICdOdW1iZXIgb2YgQ29waWVzJykgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoc3RyX3JlcGxhY2VfYWxsKHgsICJmb28iLCAiICIpLCB3aWR0aCA9IDE3KSkKY3Jvc3NfZnQgPC0gYnNjb2xzKAogIGZpbHRlcl9zZWxlY3QoInB1Ymxpc2hlciIsICJTZWxlY3QgYSBwdWJsaXNoZXIiLCBmdCwgfnB1Ymxpc2hlciksCiAgZ2dwbG90bHkoZ2dfZnQsIGR5bmFtaWNUaWNrcyA9IFRSVUUpLAogIHdpZHRocyA9IGMoMTIsIDEyKQopCgpic2NvbHMoY3Jvc3NfZnQpCgojIHNoYXJlZF9ldXJvX3B1Ymxpc2hlciA8LSBTaGFyZWREYXRhJG5ldyhmaWx0ZXJlZCkKIyBsZWFmbGV0KHNoYXJlZF9ldXJvX3B1Ymxpc2hlcikgJT4lIGFkZE1hcmtlcnMoKQojIGRhdGEudGFibGU6OmRhdGEudGFibGUoc2hhcmVkX2V1cm9fcHVibGlzaGVyKQoKCmBgYAojIyBJZGVhOiBQdWJsaXNoZXJzJyBDaGFyZ2VzIHZzLiBZZWFyL09BIFR5cGUKIyMgU3ViLVF1ZXN0aW9uOiBXaGF0IGJlc3QgZXhwbGFpbnMgdGhlIHBhcnRpY3VsYXIgZGl2aXNpb24gb2YgY2hhcmdlcz8gKFllYXIsIE9BIFR5cGUpCiMjIE9ic2VydmF0aW9uOiBUaGUgbG93IGFuZCBoaWdoIGNoYXJnZSBncm91cHMgc2VlbSB0byBiZSBkZWZpbmVkIGJ5IHRoZSB0eXBlIG9mIE9BIGJ1c2luZXNzIG1vZGVsLCB3aGVyZWFzIHRoZSBzbGlnaHQgZGlmZmVyZW5jZXMgd2l0aGluIGVhY2ggZ3JvdXAgc2VlbSB0byBiZSBkZWZpbmVkIGJ5IHRoZSB5ZWFyLiAKYGBge3IgIH0KCmhlYWQoZmlsdGVyZWQpCgojIERvZXMgVHlwZSBvZiBPQSBpbXBhY3QgdGhlIHBhcnRpY3VsYXIgZGl2aXNpb24gb2YgY2hhcmdlcz8KCmV1cm9fb2FfcHVibGlzaGVyIDwtIGZpbHRlcmVkICU+JSAKICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkLCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSwgeSA9IGZpbHRlcmVkJGV1cm8pLCAKICAgICAgICAgYWVzKHggPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSwgeSA9IGZpbHRlcmVkJGV1cm8pKSArIAogIGdlb21fY291bnQoYWVzKGNvbG9yID0gLi5uLi4sIGdyb3VwID0gZXVybykpICsgCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTApICsgCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgT0EgSW1wYWN0cyBQcmljZSBEaXZpc2lvbiBvZiBDaGFyZ2VzIiwgeCA9ICJUeXBlIG9mIE9BIiwgeSA9ICJQcmljZSAoRXVybykiLCBjb2xvciA9ICdOdW1iZXIgb2YgQ29waWVzJykKCiMgZ2dwbG90OgpnZ3Bsb3RseShldXJvX29hX3B1Ymxpc2hlcikKCiMgY3Jvc3N0YWxrOgpmdCA8LSBoaWdobGlnaHRfa2V5KGZpbHRlcmVkKQpnZ19mdCA8LSBnZ3Bsb3QoZGF0YSA9IGZ0LCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSwgeSA9IGZpbHRlcmVkJGV1cm8pKSArIAogIGdlb21fY291bnQoYWVzKGNvbG9yID0gLi5uLi4sIHNpemUgPSBhZnRlcl9zdGF0KHByb3ApLCBncm91cCA9IGV1cm8pKSArIAogIGxhYnModGl0bGUgPSAiSG93IE9BIEltcGFjdHMgRGl2aXNpb24gb2YgQ2hhcmdlcyIsIHggPSAiVHlwZSBvZiBPQSIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpCmNyb3NzX2Z0IDwtIGJzY29scygKICBmaWx0ZXJfc2VsZWN0KCJwdWJsaXNoZXIiLCAiU2VsZWN0IGEgcHVibGlzaGVyIiwgZnQsIH5wdWJsaXNoZXIpLAogIGdncGxvdGx5KGdnX2Z0LCBkeW5hbWljVGlja3MgPSBUUlVFKSwKICB3aWR0aHMgPSBjKDEyLCAxMikKKQoKYnNjb2xzKGNyb3NzX2Z0KQoKCiMgRG9lcyBZZWFyIGltcGFjdCB0aGUgcGFydGljdWxhciBkaXZpc2lvbiBvZiBjaGFyZ2VzPwoKZXVyb195ZWFyX3B1Ymxpc2hlciA8LSBmaWx0ZXJlZCAlPiUgCiAgZ2dwbG90KGRhdGEgPSBmaWx0ZXJlZCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcGVyaW9kLCB5ID0gZmlsdGVyZWQkZXVybyksIAogICAgICAgICBhZXMoeCA9IGZpbHRlcmVkJHBlcmlvZCwgeSA9IGZpbHRlcmVkJGV1cm8pKSArIAogIGdlb21fY291bnQoYWVzKGNvbG9yID0gLi5uLi4sIGdyb3VwID0gZXVybykpICsgCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTApICsgCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgWWVhciBJbXBhY3RzIFByaWNlIERpdmlzaW9uIG9mIENoYXJnZXMiLCB4ID0gIlllYXIiLCB5ID0gIlByaWNlIChFdXJvKSIsIGNvbG9yID0gJ051bWJlciBvZiBDb3BpZXMnKQoKIyBnZ3Bsb3Q6CmdncGxvdGx5KGV1cm9feWVhcl9wdWJsaXNoZXIpCgojIGNyb3NzdGFsazoKZnQgPC0gaGlnaGxpZ2h0X2tleShmaWx0ZXJlZCkKZ2dfZnQgPC0gZ2dwbG90KGRhdGEgPSBmdCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcGVyaW9kLCB5ID0gZmlsdGVyZWQkZXVybykpICsgCiAgZ2VvbV9jb3VudChhZXMoY29sb3IgPSAuLm4uLiwgc2l6ZSA9IGFmdGVyX3N0YXQocHJvcCksIGdyb3VwID0gZXVybykpICsgCiAgbGFicyh0aXRsZSA9ICJIb3cgWWVhciBJbXBhY3RzIERpdmlzaW9uIG9mIENoYXJnZXMiLCB4ID0gIlllYXIiLCB5ID0gIlByaWNlIChFdXJvKSIsIGNvbG9yID0gJ051bWJlciBvZiBDb3BpZXMnKQpjcm9zc19mdCA8LSBic2NvbHMoCiAgZmlsdGVyX3NlbGVjdCgicHVibGlzaGVyIiwgIlNlbGVjdCBhIHB1Ymxpc2hlciIsIGZ0LCB+cHVibGlzaGVyKSwKICBnZ3Bsb3RseShnZ19mdCwgZHluYW1pY1RpY2tzID0gVFJVRSksCiAgd2lkdGhzID0gYygxMiwgMTIpCikKCmJzY29scyhjcm9zc19mdCkKCgpgYGAKIyMgSWRlYTogUHVibGlzaGVycyB2cy4gT0EKIyMgUXVlc3Rpb246IFdoYXQgdHlwZSBvZiBidXNpbmVzcyBtb2RlbCBkbyB0aGUgdG9wIDI1JSBwdWJsaXNoZXJzIHVzZT8KIyMgT2JzZXJ2YXRpb246IE1vc3QgaGF2ZSBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIFRydWUgKG1vdmVkIHRvIE9BIGZyb20gdHJhZGl0aW9uYWwgcHVibGlzaGluZykgdGhhbiBGYWxzZSAoYWxyZWFkeSBwdWJsaXNoZWQgT0EpLgpgYGB7ciAgfQoKb2FfdHlwZSA8LSBmaWx0ZXJlZCAlPiUgCiAgZ2dwbG90KGRhdGEgPSBmaWx0ZXJlZCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcHVibGlzaGVyLCBjb2xvdXIgPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSksIGZpbGwgPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiLCB3aWR0aCA9IDAuNywgZmlsbD0iI0VBRUFFQSIpICsKICBsYWJzKHRpdGxlID0gIkJ1c2luZXNzIE1vZGVsIE9BIGZvciBQdWJsaXNoZXJzIiwgeCA9ICJUb3AgMjUlIG9mIFB1Ymxpc2hlcnMiLCB5ID0gIlByb3BvcnRpb24iLCBjb2xvciA9ICdUeXBlcyBvZiBPQScpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHN0cl9yZXBsYWNlX2FsbCh4LCAiZm9vIiwgIiAiKSwgd2lkdGggPSAxNykpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKCmdncGxvdGx5KG9hX3R5cGUpCgojIGNyb3NzdGFsazoKZnQgPC0gaGlnaGxpZ2h0X2tleShmaWx0ZXJlZCkKb2FfZnQgPC0gZ2dwbG90KGRhdGEgPSBmdCwgbWFwcGluZyA9IGFlcyh4ID0gZnQkcHVibGlzaGVyLCBjb2xvdXIgPSBmdCRiYWNrbGlzdF9vYSksIGZpbGwgPSBmdCRiYWNrbGlzdF9vYSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiLCB3aWR0aCA9IDAuNykgKwogIGxhYnModGl0bGUgPSAiQnVzaW5lc3MgTW9kZWwgT0EgZm9yIFB1Ymxpc2hlcnMiLCB4ID0gIlRvcCAyNSUgb2YgUHVibGlzaGVycyIsIHkgPSAiUHJvcG9ydGlvbiBvZiBCYWNrbGlzdCBPQSIsIGNvbG9yID0gJ1R5cGVzIG9mIE9BJykgKwogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoc3RyX3JlcGxhY2VfYWxsKHgsICJmb28iLCAiICIpLCB3aWR0aCA9IDE3KSkKIyBjcm9zc19vYV9mdCA8LSBic2NvbHMoCiMgICBmaWx0ZXJfc2VsZWN0KCJwdWJsaXNoZXIiLCAiU2VsZWN0IGEgcHVibGlzaGVyIiwgZnQsIH5wdWJsaXNoZXIpLAojICAgZ2dwbG90bHkob2FfZnQsIGR5bmFtaWNUaWNrcyA9IFRSVUUpLAojICAgIyB3aWR0aHMgPSBjKDEyLCAxMikKIyApCgojIGJzY29scyhjcm9zc19vYV9mdCkKCgpgYGAKIyMgSWRlYTogUHVibGlzaGVycycgT0EgdnMuIFllYXIKIyMgUXVlc3Rpb246IERpZCBPQSBidXNpbmVzcyBtb2RlbHMgb2YgdGhlIHRvcCAyNSUgcHVibGlzaGVycyBjaGFuZ2UgcGVyIHllYXI/CiMjIE9ic2VydmF0aW9uOgpgYGB7ciAgfQoKb2FfdGltZSA8LSBmdW5jdGlvbihwdWJfbmFtZSkgewogIHB1Yl9mdCA8LSBmaWx0ZXIoZmlsdGVyZWQsIGZpbHRlcmVkJHB1Ymxpc2hlciA9PSBwdWJfbmFtZSkKICAKICBwdWJfb2EgPC0gcHViX2Z0ICU+JSAKICAgIGdncGxvdChkYXRhID0gcHViX2Z0LCBtYXBwaW5nID0gYWVzKHggPSBwdWJfZnQkcGVyaW9kLCBjb2xvdXIgPSBwdWJfZnQkYmFja2xpc3Rfb2EpLCBmaWxsID0gcHViX2Z0JGJhY2tsaXN0X29hKSArCiAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIiwgd2lkdGggPSAwLjcsIGZpbGw9IiNFQUVBRUEiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUocHViX25hbWUsICIncyBPQSBUaHJvdWdoIHRoZSBZZWFycyIsIHNlcCA9ICIiKSwgCiAgICAgICAgIHggPSAiWWVhcnMiLCB5ID0gIlByb3BvcnRpb24gb2YgQmFja2xpc3QgT0EiLCBjb2xvciA9ICdUeXBlcyBvZiBPQScpICsKICAgIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHM9YygyMDE3LCAyMDE4LCAyMDE5KSkgKwogICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpCgogIGdncGxvdGx5KHB1Yl9vYSkKICAKfQoKdG9wMjVfbGlzdCA9IGMoInRyYW5zY3JpcHQgVmVybGFnIiwgIkR1a2UgVW5pdmVyc2l0eSBQcmVzcyIsICJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIFByZXNzIiwgIk1hbmNoZXN0ZXIgVW5pdmVyc2l0eSBQcmVzcyIsICJQbHV0byBQcmVzcyIsICJMaXZlcnBvb2wgVW5pdmVyc2l0eSBQcmVzcyIpCgpvYV90aW1lKCJ0cmFuc2NyaXB0IFZlcmxhZyIpCgpvYV90aW1lKCJEdWtlIFVuaXZlcnNpdHkgUHJlc3MiKQoKb2FfdGltZSgiVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBQcmVzcyIpCgpvYV90aW1lKCJNYW5jaGVzdGVyIFVuaXZlcnNpdHkgUHJlc3MiKQoKb2FfdGltZSgiUGx1dG8gUHJlc3MiKQoKb2FfdGltZSgiTGl2ZXJwb29sIFVuaXZlcnNpdHkgUHJlc3MiKQoKYGBgCiMjIElkZWE6IFJldmVudWUgdnMuIE9BCiMjIFF1ZXN0aW9uOiBXaGF0IHRvdGFsIHJldmVudWUgYXJlIHB1Ymxpc2hlcnMgcmVjZWl2aW5nIGVhY2ggeWVhcj8KIyMgT2JzZXJ2YXRpb246IApgYGB7ciAgfQoKIyBGaW5kaW5nIHRvdGFsIHJldmVudWUgZm9yIGVhY2ggcHVibGlzaGVyCgpyZXZlbnVlX2ZpbmRlciA8LSBmdW5jdGlvbihwdWJfbmFtZSkgewogIHB1Yl9maWx0ZXJlZCA8LSBmaWx0ZXIoZmlsdGVyZWQsIGZpbHRlcmVkJHB1Ymxpc2hlciA9PSBwdWJfbmFtZSkKICByZXYgPSBzdW0ocHViX2ZpbHRlcmVkJGV1cm8pCn0KCnJldmVudWVfZGYgPC0gZGF0YS5mcmFtZSgicHVibGlzaGVyIiA9IHRvcDI1X2xpc3QpCnJldmVudWVfbGlzdCA8LSBjKCkKCmZvciAoaSBpbiB0b3AyNV9saXN0KSB7CiAgcmV2ZW51ZV9saXN0PC1jKHJldmVudWVfbGlzdCxyZXZlbnVlX2ZpbmRlcihpKSkKfQoKcmV2ZW51ZV9kZiRyZXZlbnVlIDwtIGMocmV2ZW51ZV9saXN0KQpwcmludChyZXZlbnVlX2RmKQoKIyBnZ3Bsb3Q6CnB1Ymxpc2hlcl9yZXZlbnVlIDwtIHJldmVudWVfZGYgJT4lCiAgZ2dwbG90KGRhdGEgPSByZXZlbnVlX2RmLCBtYXBwaW5nID0gYWVzKHggPSByZXZlbnVlX2RmJHB1Ymxpc2hlciwgeSA9IHJldmVudWVfZGYkcmV2ZW51ZSksIGZpbGwgPSByZXZlbnVlX2RmJHJldmVudWUpICsKICBnZW9tX2NvbCgpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIFJldmVudWUgZm9yIFB1Ymxpc2hlcnMiLCB4ID0gIlRvcCAyNSUgb2YgUHVibGlzaGVycyIsIHkgPSAiUmV2ZW51ZSAoRXVybykiLCBjb2xvciA9ICdUeXBlcyBvZiBPQScpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHN0cl9yZXBsYWNlX2FsbCh4LCAiZm9vIiwgIiAiKSwgd2lkdGggPSAxNykpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQoKZ2dwbG90bHkocHVibGlzaGVyX3JldmVudWUpCgoKYGBgCiMjIElkZWE6IFJldmVudWUgdnMuIE9BCiMjIFF1ZXN0aW9uOiBXaGF0IHJldmVudWUgYXJlIHB1Ymxpc2hlcnMgcmVjZWl2aW5nIHBlciB5ZWFyPwojIyBPYnNlcnZhdGlvbjogCmBgYHtyICB9CgojIEZpbmRpbmcgdG90YWwgcmV2ZW51ZSBmb3IgZWFjaCBwdWJsaXNoZXIKCnJldmxpc3RfMjAxNyA8LSBjKCkKcmV2bGlzdF8yMDE4IDwtIGMoKQpyZXZsaXN0XzIwMTkgPC0gYygpCgpyZXZsaXN0IDwtIGMoKQoKZm9yIChuYW1lIGluIHRvcDI1X2xpc3QpIHsKICBwdWJfbmFtZSA8LSBmaWx0ZXIoZmlsdGVyZWQsIGZpbHRlcmVkJHB1Ymxpc2hlciA9PSBuYW1lKQogIHJldl8yMDE3ID0gc3VtKHB1Yl9uYW1lW3B1Yl9uYW1lJHBlcmlvZCA9PSAyMDE3LF0kZXVybykKICByZXZsaXN0XzIwMTcgPC0gYyhyZXZsaXN0XzIwMTcsIHJldl8yMDE3KQogIHJldl8yMDE4ID0gc3VtKHB1Yl9uYW1lW3B1Yl9uYW1lJHBlcmlvZCA9PSAyMDE4LF0kZXVybykKICByZXZsaXN0XzIwMTggPC0gYyhyZXZsaXN0XzIwMTgsIHJldl8yMDE4KQogIHJldl8yMDE5ID0gc3VtKHB1Yl9uYW1lW3B1Yl9uYW1lJHBlcmlvZCA9PSAyMDE5LF0kZXVybykgCiAgcmV2bGlzdF8yMDE5IDwtIGMocmV2bGlzdF8yMDE5LCByZXZfMjAxOSkKfQoKcmV2ZW51ZV9kZiA8LSBkYXRhLmZyYW1lKCJwdWJsaXNoZXIiID0gdG9wMjVfbGlzdCkKcmV2ZW51ZV9kZiQnMjAxNycgPC0gYyhyZXZsaXN0XzIwMTcpCnJldmVudWVfZGYkJzIwMTgnIDwtIGMocmV2bGlzdF8yMDE4KQpyZXZlbnVlX2RmJCcyMDE5JyA8LSBjKHJldmxpc3RfMjAxOSkKCnByaW50KHJldmVudWVfZGYpCgpyZXZlbnVlX3llYXIgPC0gYyhyZXZlbnVlX2RmJCcyMDE3JywgcmV2ZW51ZV9kZiQnMjAxOCcsIHJldmVudWVfZGYkJzIwMTknKQp5ZWFyIDwtIGMoJzIwMTcnLCAnMjAxOCcsICcyMDE5JykKCiMgZ2dwbG90OgpwdWJfeWVhcl9yZXZlbnVlMSA8LSByZXZlbnVlX2RmICU+JQogIAogIGdncGxvdChkYXRhID0gcmV2ZW51ZV9kZiwgbWFwcGluZyA9IGFlcyh4ID0gJzIwMTcnLCB5ID0gcmV2ZW51ZV9kZiQnMjAxNycsIGZpbGwgPSByZXZlbnVlX2RmJHB1Ymxpc2hlcikpICsKICBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIFJldmVudWUgZm9yIFB1Ymxpc2hlcnMiLCB4ID0gIlllYXIiLCB5ID0gIlJldmVudWUgKEV1cm8pIiwgY29sb3IgPSAnUHVibGlzaGVycycpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpCgpnZ3Bsb3RseShwdWJfeWVhcl9yZXZlbnVlMSkKCnB1Yl95ZWFyX3JldmVudWUyIDwtIHJldmVudWVfZGYgJT4lCiAgCiAgZ2dwbG90KGRhdGEgPSByZXZlbnVlX2RmLCBtYXBwaW5nID0gYWVzKHggPSAnMjAxOCcsIHkgPSByZXZlbnVlX2RmJCcyMDE4JywgZmlsbCA9IHJldmVudWVfZGYkcHVibGlzaGVyKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uPSJkb2RnZSIsIHN0YXQ9ImlkZW50aXR5IikgKwogIGxhYnModGl0bGUgPSAiVG90YWwgUmV2ZW51ZSBmb3IgUHVibGlzaGVycyIsIHggPSAiWWVhciIsIHkgPSAiUmV2ZW51ZSAoRXVybykiLCBjb2xvciA9ICdQdWJsaXNoZXJzJykgKwogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkKCmdncGxvdGx5KHB1Yl95ZWFyX3JldmVudWUyKQoKcHViX3llYXJfcmV2ZW51ZTMgPC0gcmV2ZW51ZV9kZiAlPiUKICAKICBnZ3Bsb3QoZGF0YSA9IHJldmVudWVfZGYsIG1hcHBpbmcgPSBhZXMoeCA9ICcyMDE5JywgeSA9IHJldmVudWVfZGYkJzIwMTknLCBmaWxsID0gcmV2ZW51ZV9kZiRwdWJsaXNoZXIpKSArCiAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBSZXZlbnVlIGZvciBQdWJsaXNoZXJzIiwgeCA9ICJZZWFyIiwgeSA9ICJSZXZlbnVlIChFdXJvKSIsIGNvbG9yID0gJ1B1Ymxpc2hlcnMnKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKQoKZ2dwbG90bHkocHViX3llYXJfcmV2ZW51ZTMpCgoKYGBgCiMjIyBDb250aW51ZWQsIHRyaWVkIHB1dHRpbmcgaXQgaW50byBvbmUgZ3JhcGguIApgYGB7cn0KCnJldmxpc3QgPC0gYygpCnJldmxpc3RfMjAxNyA8LSBjKCkKcmV2bGlzdF8yMDE4IDwtIGMoKQpyZXZsaXN0XzIwMTkgPC0gYygpCgpmb3IgKG5hbWUgaW4gdG9wMjVfbGlzdCkgewogIHB1Yl9uYW1lIDwtIGZpbHRlcihmaWx0ZXJlZCwgZmlsdGVyZWQkcHVibGlzaGVyID09IG5hbWUpCiAgcmV2XzIwMTcgPSBzdW0ocHViX25hbWVbcHViX25hbWUkcGVyaW9kID09IDIwMTcsXSRldXJvKQogIHJldmxpc3RfMjAxNyA8LSBjKHJldmxpc3RfMjAxNywgcmV2XzIwMTcpCiAgcmV2XzIwMTggPSBzdW0ocHViX25hbWVbcHViX25hbWUkcGVyaW9kID09IDIwMTgsXSRldXJvKQogIHJldmxpc3RfMjAxOCA8LSBjKHJldmxpc3RfMjAxOCwgcmV2XzIwMTgpCiAgcmV2XzIwMTkgPSBzdW0ocHViX25hbWVbcHViX25hbWUkcGVyaW9kID09IDIwMTksXSRldXJvKQogIHJldmxpc3RfMjAxOSA8LSBjKHJldmxpc3RfMjAxOSwgcmV2XzIwMTkpCn0KCnJldmxpc3QgPC0gYyhyZXZsaXN0XzIwMTcsIHJldmxpc3RfMjAxOCwgcmV2bGlzdF8yMDE5KQoKcHJpbnQocmV2bGlzdCkKCm5yZXYgPC0gbWF0cml4KHJldmxpc3QsIG5jb2w9NiwgYnlyb3c9VFJVRSkKY29sbmFtZXMobnJldikgPC0gdG9wMjVfbGlzdApyb3duYW1lcyhucmV2KSA8LSBjKCIyMDE3IiwgIjIwMTgiLCAiMjAxOSIpCm5yZXYgPC0gYXMudGFibGUobnJldikKbnJldiA8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeChucmV2KQoKcHJpbnQobnJldikKCiMsIG5yZXYkYER1a2UgVW5pdmVyc2l0eSBQcmVzc2AsIG5yZXYkYFVuaXZlcnNpdHkgb2YgTWljaGlnYW4gUHJlc3NgLCBucmV2JGBQbHV0byBQcmVzc2AsIG5yZXYkYE1hbmNoZXN0ZXIgVW5pdmVyc2l0eSBQcmVzc2AsIG5yZXYkYExpdmVycG9vbCBVbml2ZXJzaXR5IFByZXNzYAoKcHViX3llYXJfcmV2IDwtIG5yZXYgJT4lCiAgCiAgZ2dwbG90KGRhdGEgPSBucmV2LCBtYXBwaW5nID0gYWVzKHggPSBjKCIyMDE3IiwgIjIwMTgiLCAiMjAxOSIpLCB5ID0gYyhucmV2JCJ0cmFuc2NyaXB0IFZlcmxhZyIpLCBmaWxsID0gbnJldiRwdWJsaXNoZXIpKSArCiAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBSZXZlbnVlIGZvciBQdWJsaXNoZXJzIiwgeCA9ICJZZWFyIiwgeSA9ICJSZXZlbnVlIChFdXJvKSIsIGNvbG9yID0gJ1B1Ymxpc2hlcnMnKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKQoKZ2dwbG90bHkocHViX3llYXJfcmV2KQoKYGBgCiMjIElkZWE6IERPQUIgYW5hbHlzaXMKIyMgUXVlc3Rpb246IFdoYXQgaXMgdGhlIGF2ZXJhZ2UgdGltZSBnYXAgYmV0d2VlbiB5ZWFyIG9mIHB1YmxpY2F0aW9uIGFuZCBhZGRlZCBvbiBkYXRlPyAKIyMgT2JzZXJ2YXRpb246IApgYGB7cn0KCkRPQUJtZXRhLmRmIDwtIGZpbHRlcihET0FCbWV0YS5kZiwgaXMubmEoRE9BQm1ldGEuZGYkWWVhci5vZi5wdWJsaWNhdGlvbikpCnByaW50KERPQUJtZXRhLmRmJFllYXIub2YucHVibGljYXRpb25bMTo0XSkKZ2FwID0gbWVhbihET0FCbWV0YS5kZiRBZGRlZC5vbi5kYXRlIC0gRE9BQm1ldGEuZGYkWWVhci5vZi5wdWJsaWNhdGlvblsxOjNdKQpwcmludChnYXApCgpgYGAKIyMjIENvbXBhcmlzb24gb2YgY2hhcmdlcyBieSB5ZWFyIGFuZCBiYWNrbGlzdApgYGB7cn0KIyBjcmVhdGUgZmFjZXRlZCBwbG90IG9iamVjdApjaGFyZ2VzLnBsb3QgPC0gS1VicGMuZGYgJT4lIGdncGxvdChhZXMoZXVybykpK2dlb21faGlzdG9ncmFtKGJpbnM9NikrZmFjZXRfZ3JpZChyb3dzPXZhcnMocGVyaW9kKSwgY29scyA9IHZhcnMoYmFja2xpc3Rfb2EpKQoKCiMjIFByZXNlbnQgYXMgU3RhbmRhcmQgcGxvdAogcGxvdChjaGFyZ2VzLnBsb3QpCgojIHRoaXMgcGxvdCB3aWxsIHJlbmRlciBwdWJsaWNseSBodHRwczovL2h0bWxwcmV2aWV3LmdpdGh1Yi5pby8/aHR0cHM6Ly9naXRodWIuY29tL01JVC1JbmZvcm1hdGljcy9tb25vZ3JhcGgvYmxvYi9tYXN0ZXIvMDAlMjBFREElMjBTdGFydC5uYi5odG1sCgpgYGAKIyMjIEludGVyYWN0aXZlIGNoYXJnZXMgZXhwbG9yYXRpb24KYGBge3J9CiBnZ3Bsb3RseShjaGFyZ2VzLnBsb3QpCiMgaHR0cHM6Ly9taXQtaW5mb3JtYXRpY3MuZ2l0aHViLmlvL21vbm9ncmFwaC9kZW1vLmh0bWwKCmBgYApgYGAKIyMjIEludGVyYWN0aXZlIERhdGFzZXQgRXhwbG9yYXRpb24gCmBgYApgYGB7cn0KS1VicGMuZGYgJT4lIEV4UGFuRChkZj0uICAgICAgICx0aXRsZT0iS1UgQm9vayBQcm9jZXNzaW5nIENoYXJnZXMiLGV4cG9ydF9uYl9vcHRpb24gPSBUUlVFKQojIEV4UGFuRCB1c2VzIHNoaW55KCkgd2hpY2ggd29ya3MgcnVubmluZyBSIGxvY2FsbHksIGJ1dCBpc24ndCBnb2luZyB0byB3b3JrIHRocm91Z2ggZ2l0aHViLiBDb3VsZCBwdWJsaXNoIHRocm91Z2ggc2hpbnlhcHBzLmlvIChsb3cgdXNhZ2Ugb25seSksIG9yIGV4cG9ydCAgYSBub24taW50ZXJhY3RpdmUgbm90ZWJvb2sgaXQKIyBzZWU6IGh0dHBzOi8vZHJtYWx0bWFuLnNoaW55YXBwcy5pby9kZW1vLwpgYGAKCg==